package br.com.beyondclick.model.entity;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import br.com.gentech.commons.model.entity.BusinessException;
import br.com.gentech.commons.model.entity.IdentifiableEntity;

/**
 * @author AC de Souza
 *
 */
@Entity
@NamedQueries({
	@NamedQuery(name="findComSaldoDisponivel", query="from Cheque c where c.valor > c.saldoGasto or	c.saldoGasto is null")
	,@NamedQuery(name="findPorPeriodoOrdenadoPorEmissao", query="from Cheque c where c.emissao between :inicio and :fim order by c.emissao")
})
public class Cheque implements IdentifiableEntity<Long> {
	private static final long serialVersionUID = 1L;
	
	public enum Cheques {
		findComSaldoDisponivel,
		findPorPeriodoOrdenadoPorEmissao
	}

	@Id @GeneratedValue
	private Long id;
	private long numero;
	private BigDecimal valor;
	@Temporal(TemporalType.DATE)
	private Date emissao;
	@Temporal(TemporalType.DATE)
	private Date vencimento;
	private String nominal;
	private String observacao;
	private BigDecimal saldoGasto;

	@Transient
	private List<CompraPedidoMaterial> compraPedidoMateriais;

	/**
	 * Avaliar se o saldo do objeto {@link Cheque} já foi totalmente gasto na {@link Compra} de objetos {@link Material},
	 * para atender a objetos {@link Pedido}.
	 *  
	 * @param totalCompras Valor total da {@link Compra} que se deseja saber se o {@link Cheque} tem fundos para realizar.
	 * 
	 * @return
	 * <ul>
	 *  <li>{@link Boolean#TRUE} Para o caso do valor, já gasto do {@link Cheque}, menos o totalCompras ser menor do que o saldo do cheque.</li>
	 *  <li>{@link Boolean#FALSE} Para o caso do valor, já gasto do {@link Cheque}, menos o totalCompras ser menor do que o saldo do cheque.</li>
	 * </ul> 
	 */
	public boolean isSaldoSuficiente(final BigDecimal totalCompras){
		BigDecimal saldoTotal = new BigDecimal(BigInteger.ZERO, 2);
		saldoTotal = totalCompras.add( this.getSaldoGasto() );

		return (saldoTotal.compareTo( this.getValor() ) <= 0);
	}

	/**
	 * Debitar o valor informado do total disponível neste {@link Cheque}.
	 * 
	 * @param valorParaDebitar Valor a ser debitado do total deste {@link Cheque}.
	 * @throws BusinessException Quando o valor total a ser debitado for maior do que o saldo do cheque.
	 */
	public void debitar(final BigDecimal valorParaDebitar) throws BusinessException{
		if( isSaldoSuficiente( valorParaDebitar ) ){
			BigDecimal valorTotalParaDebitar =  new BigDecimal(BigInteger.ZERO, 2);
			valorTotalParaDebitar = valorParaDebitar.add(getSaldoGasto());
			setSaldoGasto( valorTotalParaDebitar );
		}else{
			BusinessException valorCompraMaiorSaldoCheque = new BusinessException("Valor total da compra é maior do que o valor disponível no cheque.");
			valorCompraMaiorSaldoCheque.setLocalizedMsgKey("bizrule.ValorTotalCompraMaiorValorDisponivelCheque");
			valorCompraMaiorSaldoCheque.setLocalizedMsgArgs(new String[]{getNumero()+""});
			throw valorCompraMaiorSaldoCheque;
		}
	}
	public void creditar(BigDecimal credito) {
		BigDecimal saldoUsadoDoChequeMenosCreditoRecebido =  new BigDecimal(BigInteger.ZERO, 2);
		saldoUsadoDoChequeMenosCreditoRecebido = getSaldoGasto().subtract( credito );
		setSaldoGasto( saldoUsadoDoChequeMenosCreditoRecebido );
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + (int) (id ^ (id >>> 32));
		return result;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final Cheque other = (Cheque) obj;
		if ( !getId().equals(other.getId()) )
			return false;
		return true;
	}

	/**
	 * @see IdentifiableEntity#getOID();
	 */
	public Long getOID() {
		return getId();
	}
	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(long id) {
		this.id = id;
	}

	/**
	 * @return the valor
	 */
	public BigDecimal getValor() {
		if(valor == null ) valor = new BigDecimal(BigInteger.ZERO, 2);
		return valor;
	}

	/**
	 * @param valor the valor to set
	 */
	public void setValor(BigDecimal valor) {
		this.valor = valor;
	}

	/**
	 * @return the numero
	 */
	public long getNumero() {
		return numero;
	}

	/**
	 * @param numero the numero to set
	 */
	public void setNumero(long numero) {
		this.numero = numero;
	}

	/**
	 * @return the emissao
	 */
	public Date getEmissao() {
		if(emissao == null)
			emissao = new Date();

		return emissao;
	}

	/**
	 * @param emissao the emissao to set
	 */
	public void setEmissao(Date emissao) {
		this.emissao = emissao;
	}

	/**
	 * @return the nominal
	 */
	public String getNominal() {
		return nominal;
	}

	/**
	 * @param nominal the nominal to set
	 */
	public void setNominal(String nominal) {
		this.nominal = nominal;
	}

	/**
	 * @return the observacao
	 */
	public String getObservacao() {
		return observacao;
	}

	/**
	 * @param observacao the observacao to set
	 */
	public void setObservacao(String observacao) {
		this.observacao = observacao;
	}

	/**
	 * @return the vencimento
	 */
	public Date getVencimento() {
		return vencimento;
	}

	/**
	 * @param vencimento the vencimento to set
	 */
	public void setVencimento(Date vencimento) {
		this.vencimento = vencimento;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return the compras
	 */
	public List<CompraPedidoMaterial> getCompraPedidoMateriais() {
		return compraPedidoMateriais;
	}

	/**
	 * @param compras the compras to set
	 */
	public void setCompraPedidoMateriais(List<CompraPedidoMaterial> compraPedidoMateriais) {
		this.compraPedidoMateriais = compraPedidoMateriais;
	}

	/**
	 * @return the saldoDisponivel
	 */
	public BigDecimal getSaldoGasto() {
		if(saldoGasto == null ) saldoGasto = new BigDecimal(BigInteger.ZERO, 2);
		return saldoGasto;
	}

	/**
	 * @param saldoDisponivel the saldoDisponivel to set
	 */
	public void setSaldoGasto(BigDecimal saldoDisponivel) {
		this.saldoGasto = saldoDisponivel;
	}
}
